#include "coroutine.h"
#include "base.h"
#include "mycoroutine_coroutine.h"

void* coroutine_get_current_task(){
	return coroG.current ? coroG.current->get_task(coroG.current) : NULL;
}

void* coroutine_get_task(Coroutine *coroutine){
	return coroutine->task;
}

long coroutine_create(coroutine_func_t fn,void *private_data){
	long cid;

	Coroutine *coro = coroutine_init(fn,private_data);

	cid = coroutine_run(coro);

	return cid;
}

Coroutine *coroutine_init(coroutine_func_t fn,void *private_data){
	Coroutine *coro = (Coroutine *)malloc(sizeof(Coroutine));
	if(!coro){
		printf("malloc error\n");
		exit(-1);
	}

	coro->stack_size = DEFAULT_C_STACK_SIZE;
	coro->cid = Coroutine_array_push(coro);
	coro->task = NULL;
	coro->origin = NULL;
	coro->on_close = PHPCoroutine_on_close;
	coro->get_task = coroutine_get_task;

	coro->ctx = context_init(fn,coro->stack_size,private_data);

	return coro;
}

void coroutine_dest(Coroutine *coro){
	context_dest(coro->ctx);
	free(coro);
}

long coroutine_run(Coroutine *coro){
	long cid = coro->cid;
	coro->origin = coroG.current;
	coroG.current = coro;
	coro->ctx->swap_in(coro->ctx);
	coroutine_check_end(coro);
	return cid;
}

void set_task(Coroutine * coro,void *_task){
	coro->task = _task;
}

void coroutine_check_end(Coroutine *coro){
	if(context_is_end(coro->ctx)){
		coroutine_close(coro);
	}
}

void coroutine_close(Coroutine *coro){
	if(coro->on_close){
		coro->on_close(coro->task);
	}

	coroG.current = coro->origin;

	long cid = coroutine_get_cid(coro);
	Coroutine_array_erase(coroG.coroutines.coroutines,cid);
	coroutine_dest(coro);
}

Coroutine* coroutine_get_origin(Coroutine *coro){
	return coro->origin;
}

long coroutine_get_cid(Coroutine *co){
	return co->cid;
}

void coroutine_yield(Coroutine *co){
	if(coroG.on_yield){
		coroG.on_yield(co->task);
	}
	coroG.current = co->origin;
	co->ctx->swap_out(co->ctx);
}

void coroutine_resume(Coroutine *co){
	if(coroG.on_resume){
		coroG.on_resume(co->task);
	}
	
	co->origin = coroG.current;
	coroG.current = co;
	co->ctx->swap_in(co->ctx);
	coroutine_check_end(co);
}
